﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGenerics.DataStructures.Queues;

namespace MagicArenaFramework.GameObject.Components
{
    /// <summary>
    /// GoComposite is a GoComponent which can have children.
    /// It handles calling the Update, Draw and OnGui methods of all children.
    /// In most situations a GoComposite will be treated just like a GoComponent with children
    /// This implementation keeps all delegates registered by their children locally
    /// This means the parent will be calling Update, Draw and OnGui on the children
    /// 
    /// All children must be of unique type. This constraint may be removed later, but for now it stays.
    /// 
    /// Delegate registering is overrloaded with priorities. By default, all priorities are byte.MaxValue/2.
    /// The priority queue used is minimum ordered so priority 1 happens before priority 2
    /// For more about changing delegate method priority see the Component class
    /// </summary>
    public abstract class GoComposite : GoComponent
    {
        #region Properties
        public override bool isActive
        {
            get
            {
                return base.isActive;
            }
            set
            {
                foreach (var c in Children) c.isActive = value;
                base.isActive = value;
            }
        }
        public override GoComponent.GoMethod DrawDel
        {
            get
            {
                return base.DrawDel;
            }
            set
            {
                base.DrawDel = value;
            }
        }
        public override GoComponent.GoMethod GuiDel
        {
            get
            {
                return base.GuiDel;
            }
            set
            {

                base.GuiDel = value;
            }
        }
        public override GoComponent.GoMethod UpdateDel
        {
            get
            {
                return base.UpdateDel;
            }
            set
            {

                //if (value != null) registerUpdate(value);
                //else unregisterUpdate(value);
                base.UpdateDel = value;
            }
        }
        #endregion

        #region Child manipulation
        Dictionary<Type,GoComponent> _Children = new Dictionary<Type, GoComponent>(5);
        public virtual List<GoComponent> Children { get { return _Children.Values.ToList(); } }
        public virtual bool AddChild(GoComponent child)
        {
            Type type = child.GetType();
            if (!_Children.ContainsKey(type))
            {
                _Children.Add(type, child);
                child.RegisterSelf(this);
                return true;
            }
            return false;
            
            
        }
        public virtual bool RemoveChild(GoComponent child)
        {
            if (_Children.Remove(child.GetType()))
            {
                child.UnRegisterSelf(this);
                return true;
            }
            return false;
        }
        public virtual bool GetChild(Type type, out GoComponent child)
        {
            return _Children.TryGetValue(type, out child);
        }
        #endregion

        #region GoComponent methods to be overriden
        public override void Update(float dt)
        {
            if (UpdateDel != null) UpdateDel(dt);
            if(_UpdateMethods != null)
                foreach (var m in _UpdateMethods) m(dt);
        }

        public override void Draw(float dt)
        {
            if (DrawDel != null) Draw(dt);
            if(_DrawMethods !=null)
                foreach (var m in _DrawMethods) m(dt);
        }

        public override void OnGui(float dt)
        {
            if (GuiDel != null) GuiDel(dt);
            if(_GuiMethods !=null)
                foreach (var m in _GuiMethods) m(dt);
        }
        public override void RegisterSelf(GoComposite parent)
        {
            parent.registerUpdate(Update);
            parent.registerDraw(Draw);
            parent.registerOnGui(OnGui);
        }
        public override bool UnRegisterSelf(GoComposite parent)
        {
            bool rtn = false;
            rtn |= parent.unregisterUpdate(Update);
            rtn |= parent.unregisterDraw(Draw);
            rtn |= parent.unregisterOnGui(OnGui);
            return rtn;
        }
        public override void Destroy()
        {
            _UpdateMethods = null;
            _DrawMethods = null;
            _GuiMethods = null;
            GoComponent[] arr = _Children.Values.ToArray();
            foreach (GoComponent g in arr) g.Destroy();
            _Children.Clear();
            base.Destroy();
        }
        #endregion

        #region Registering children's GOMethods
        PriorityQueue<GoMethod, byte> _UpdateMethods;
        PriorityQueue<GoMethod, byte> _DrawMethods;
        PriorityQueue<GoMethod, byte> _GuiMethods;
        public virtual void registerUpdate(GoMethod update, byte priority=byte.MaxValue/2)
        {
            if (_UpdateMethods == null) initQueue(ref _UpdateMethods) ;
            _UpdateMethods.Add(update, priority);
        }
        public bool unregisterUpdate(GoMethod update)
        {
            if (_UpdateMethods == null) return false;
            return _UpdateMethods.Remove(update);
        }
        public void registerDraw(GoMethod draw, byte priority = byte.MaxValue/2)
        {
            if (_DrawMethods == null) initQueue(ref _DrawMethods);
            _DrawMethods.Add(draw, priority);
        }
        public bool unregisterDraw(GoMethod draw)
        {
            if (_DrawMethods == null) return false;
            return _DrawMethods.Remove(draw);
        }
        public void registerOnGui(GoMethod onGui, byte priority = byte.MaxValue/2)
        {
            if (_GuiMethods == null) initQueue(ref _GuiMethods);
            _GuiMethods.Add(onGui, priority);
        }
        public bool unregisterOnGui(GoMethod onGui)
        {
            if (_GuiMethods == null) return false;
            return _GuiMethods.Remove(onGui);
        }
        void initQueue(ref PriorityQueue<GoMethod, byte> q)
        {
            q = new PriorityQueue<GoMethod, byte>(PriorityQueueType.Minimum);
            q.DefaultPriority = byte.MaxValue / 2;
        }
        #endregion
    }

    public abstract class GoCompositeMultiple : GoComposite
    {
        List<GoComponent> _Children = new List<GoComponent>(5);
        public override List<GoComponent> Children
        {
            get
            {
                return _Children;
            }
        }

        #region Child override methods
        public override bool AddChild(GoComponent child)
        {
            if (_Children.Contains(child)) return false;
            _Children.Add(child);
            child.RegisterSelf(this);
            return true;
        }
        public override bool RemoveChild(GoComponent child)
        {
            if(_Children.Remove(child)){
                child.UnRegisterSelf(this);
                return true;
            }
            return false;
        }
        public override bool GetChild(Type type, out GoComponent child)
        {
            // Find a child of same type
            child = null;
            child = _Children.Find(delegate(GoComponent c){return c.GetType().Equals(type);});
            if (child == null) return false;
            return true;
        }
        public List<GoComponent> GetAllChildren(Type type)
        {
            // Find children
            List<GoComponent> comps = Children.FindAll(delegate(GoComponent c) { return c.GetType().Equals(type); });
            return comps;
        }
        
        #endregion

        #region Otehr overrides
        public override void Destroy()
        {
            GoComponent[] gs = _Children.ToArray();
            foreach (var g in gs) g.Destroy();
            _Children.Clear();
            base.Destroy();
        }
        public override bool isActive
        {
            get
            {
                return base.isActive;
            }
            set
            {
                foreach (var c in _Children) c.isActive = value;
                base.isActive = value;
            }
        }
        #endregion
    }
}
